/**
 * Copyright 2024-2024 Huawei Technologies Co., Ltd
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "aipp_para_ndk_impl.h"

#include <map>
#include <set>
#include <string>

#include "framework/infra/log/log.h"
#include "infra/base/securestl.h"
#include "infra/base/assertion.h"
#include "model_manager/general_model_manager/ndk/hiai_ndk/hiai_ndk_create_itf.h"

using namespace std;

namespace hiai {
namespace {

Status CheckBatchIndex(uint32_t batchIndex, uint32_t batchNum)
{
    if (batchIndex >= batchNum) {
        FMK_LOGE("batchIndex [%zu] is not supported.", batchIndex);
        return FAILURE;
    }
    return SUCCESS;
}

const float kFloat16Max = 65504;
const float kFloat16Lowest = -kFloat16Max;
const uint8_t g_maxBatchNum = 127;

_Float16 SaveUint16ToFp16(uint16_t num)
{
    _Float16* uintAddr = nullptr;
    uintAddr = reinterpret_cast<_Float16*>(&num);
    return *uintAddr;
}

uint16_t SaveFp16ToUint16(_Float16 num)
{
    uint16_t* uintAddr = nullptr;
    uintAddr = reinterpret_cast<uint16_t*>(&num);
    return *uintAddr;
}

const static vector<vector<vector<int32_t>>> YUV_TO_RGB = {
    {{256, 0, 359}, {256, -88, -183}, {256, 454, 0}},
    {{298, 0, 409}, {298, -100, -208}, {298, 516, 0}},
    {{256, 0, 359}, {256, -88, -183}, {256, 454, 0}},
    {{298, 0, 460}, {298, -55, -137}, {298, 541, 0}}};

const static vector<vector<vector<int32_t>>> RGB_TO_YUV = {
    {{77, 150, 29}, {-43, -85, 128}, {128, -107, -21}},
    {{66, 129, 25}, {-38, -74, 112}, {112, -94, -18}},
    {{77, 150, 29}, {-43, -85, 128}, {128, -107, -21}},
    {{47, 157, 16}, {-26, -87, 112}, {112, -102, -10}}};

const static vector<int32_t> YUV_TO_GRAY = {256, 0, 0, 0, 0, 0, 0, 0, 0};
const static vector<int32_t> RGB_TO_GRAY = {76, 150, 30, 0, 0, 0, 0, 0, 0};

bool CheckBiasEqual(const vector<int32_t>& bias, ImageFormat& targetFormat, ImageColorSpace& colorSpace)
{
    vector<int32_t> biasValues(3, 0);   // input and output bias are 3d
    biasValues[0] = colorSpace == ImageColorSpace::JPEG ? 0 : 16;
    biasValues[1] = 128;
    biasValues[2] = 128;
    for (int32_t i = 0; i < 3; i++) {
        if (targetFormat == ImageFormat::RGB888 || targetFormat == ImageFormat::BGR888) {
            if (bias[i + 3] != biasValues[i]) {
                return false;
            }
        } else {
            if (bias[i] != biasValues[i]) {
                return false;
            }
        }
    }
    return true;
}

bool IsCscMatrixToGray(const vector<int32_t>& cscPara, ImageFormat& inputFormat)
{
    if (cscPara == YUV_TO_GRAY) {
        inputFormat = ImageFormat::YUV420SP;
        return true;
    }
    if (cscPara == RGB_TO_GRAY) {
        inputFormat = ImageFormat::XRGB8888;
        return true;
    }
    return false;
}

bool CheckYUV2RGBOrBGREqual(size_t k, size_t rgb, const vector<int32_t>& cscPara)
{
    size_t t = rgb;
    for (size_t i = 0; i < cscPara.size(); i++) {
        size_t j = i % 3;
        if (cscPara[i] != YUV_TO_RGB[k][t][j]) {
            return false;
        }
        if (j == 2 && rgb == 0) {
            t++;
        }
        if (j == 2 && rgb == 2) {
            t--;
        }
    }
    return true;
}

bool CheckRGB2YUVOrYVUEqual(size_t k, size_t yuv, const vector<int32_t>& cscPara)
{
    for (size_t i = 0; i < 3; i++) {
        if (cscPara[i] != RGB_TO_YUV[k][0][i]) {
            return false;
        }
    }
    size_t t = yuv;
    for (size_t i = 3; i < cscPara.size(); i++) {
        size_t j = i % 3;
        if (cscPara[i] != RGB_TO_YUV[k][t][j]) {
            return false;
        }
        if (j == 2 && yuv == 1) {
            t++;
        }
        if (j == 2 && yuv == 2) {
            t--;
        }
    }
    return true;
}

bool IsCscMatrixToRGBOrBGR(const vector<int>& cscPara, int keyIdx, ImageFormat& inputFormat,
    ImageFormat& targetFormat, ImageColorSpace& colorSpace)
{
    for (int k = 0; k < 4; k++) {
        for (int j = 0; j < 3; j++) {
            if (cscPara[j] != YUV_TO_RGB[k][keyIdx][j]) {
                break;
            }
            if (j == 2 && CheckYUV2RGBOrBGREqual(k, keyIdx, cscPara)) {
                inputFormat = ImageFormat::YUV420SP;
                targetFormat = keyIdx == 0 ? ImageFormat::RGB888 : ImageFormat::BGR888; // 0:RGB, 2:RGB
                colorSpace = static_cast<ImageColorSpace>(k);
                return true;
            }
        }
    }
    return false;
}

bool IsCscMatrixToYUVOrYVU(const vector<int>& cscPara, int keyIdx, ImageFormat& inputFormat,
    ImageFormat& targetFormat, ImageColorSpace& colorSpace)
{
    for (int k = 0; k < 4; k++) {
        for (int j = 0; j < 3; j++) {
            if (cscPara[j + 3] != RGB_TO_YUV[k][keyIdx][j]) { // 先比较第2组
                break;
            }
            if (j == 2 && CheckRGB2YUVOrYVUEqual(k, keyIdx, cscPara)) { // 再检查剩下2组
                inputFormat = ImageFormat::RGB888;
                targetFormat = keyIdx == 1 ? ImageFormat::YUV444SP : ImageFormat::YVU444SP; // 1:YUV, 2:YVU
                colorSpace = static_cast<ImageColorSpace>(k);
                return true;
            }
        }
    }
    return false;
}

Status ConvertCscMatric2Csc(const vector<int32_t>& cscPara,
    ImageFormat& inputFormat, ImageFormat& targetFormat, ImageColorSpace& colorSpace)
{
    if (IsCscMatrixToGray(cscPara, inputFormat)) {
        targetFormat = ImageFormat::YUV400;
        colorSpace = ImageColorSpace::JPEG;
        return SUCCESS;
    }
    if (IsCscMatrixToRGBOrBGR(cscPara, 0, inputFormat, targetFormat, colorSpace)) { // YUV -> RGB
        return SUCCESS;
    }
    if (IsCscMatrixToRGBOrBGR(cscPara, 2, inputFormat, targetFormat, colorSpace)) { // YUV -> BGR
        return SUCCESS;
    }
    if (IsCscMatrixToYUVOrYVU(cscPara, 1, inputFormat, targetFormat, colorSpace)) { // RGB -> YUV
        return SUCCESS;
    }
    if (IsCscMatrixToYUVOrYVU(cscPara, 2, inputFormat, targetFormat, colorSpace)) { // RGB -> YVU
        return SUCCESS;
    }

    return FAILURE;
}

Status UpdateCropPara(HiAI_AippParam* paraBuff, uint32_t batchIndex, const CropPara& cropPara)
{
    return HIAI_NDK_HiAIAippParam_SetCropConfig(paraBuff, batchIndex,
        cropPara.cropStartPosW, cropPara.cropStartPosH, cropPara.cropSizeW, cropPara.cropSizeH);
}

Status UpdateResizePara(HiAI_AippParam* paraBuff, uint32_t batchIndex, const ResizePara& resizePara)
{
    return HIAI_NDK_HiAIAippParam_SetResizeConfig(paraBuff, batchIndex,
        resizePara.resizeOutputSizeW, resizePara.resizeOutputSizeH);
}

Status UpdatePaddingPara(HiAI_AippParam* paraBuff, uint32_t batchIndex, const PadPara& paddingPara)
{
    HIAI_EXPECT_EXEC(HIAI_NDK_HiAIAippParam_SetPadConfig(paraBuff, batchIndex,
        paddingPara.paddingSizeLeft, paddingPara.paddingSizeRight,
        paddingPara.paddingSizeTop, paddingPara.paddingSizeBottom));

    float value = std::max(std::min(paddingPara.paddingValueChn0, kFloat16Max), kFloat16Lowest);
    uint32_t paddingValueChn0 = static_cast<uint32_t>(SaveFp16ToUint16(value));
    value = std::max(std::min(paddingPara.paddingValueChn1, kFloat16Max), kFloat16Lowest);
    uint32_t paddingValueChn1 = static_cast<uint32_t>(SaveFp16ToUint16(value));
    value = std::max(std::min(paddingPara.paddingValueChn2, kFloat16Max), kFloat16Lowest);
    uint32_t paddingValueChn2 = static_cast<uint32_t>(SaveFp16ToUint16(value));
    value = std::max(std::min(paddingPara.paddingValueChn3, kFloat16Max), kFloat16Lowest);
    uint32_t paddingValueChn3 = static_cast<uint32_t>(SaveFp16ToUint16(value));

    uint32_t chnValue[] = {paddingValueChn0, paddingValueChn1, paddingValueChn2, paddingValueChn3};

    HIAI_EXPECT_EXEC(HIAI_NDK_HiAIAippParam_SetChannelPadding(paraBuff, batchIndex, chnValue, 4));
    return SUCCESS;
}

Status UpdateDtcPara(HiAI_AippParam* paraBuff, uint32_t batchIndex, const DtcPara& dtcPara)
{
    uint32_t pixelMeanPara[4] = {
        static_cast<uint32_t>(dtcPara.pixelMeanChn0), static_cast<uint32_t>(dtcPara.pixelMeanChn1),
        static_cast<uint32_t>(dtcPara.pixelMeanChn2), static_cast<uint32_t>(dtcPara.pixelMeanChn3)};

    float dtcPixelMinChn0 = std::max(std::min(dtcPara.pixelMinChn0, kFloat16Max), kFloat16Lowest);
    float dtcPixelMinChn1 = std::max(std::min(dtcPara.pixelMinChn1, kFloat16Max), kFloat16Lowest);
    float dtcPixelMinChn2 = std::max(std::min(dtcPara.pixelMinChn2, kFloat16Max), kFloat16Lowest);
    float dtcPixelMinChn3 = std::max(std::min(dtcPara.pixelMinChn3, kFloat16Max), kFloat16Lowest);
    float pixelMinPara[4] = {dtcPixelMinChn0, dtcPixelMinChn1, dtcPixelMinChn2, dtcPixelMinChn3};

    float dtcPixelVarReciChn0 = std::max(std::min(dtcPara.pixelVarReciChn0, kFloat16Max), kFloat16Lowest);
    float dtcPixelVarReciChn1 = std::max(std::min(dtcPara.pixelVarReciChn1, kFloat16Max), kFloat16Lowest);
    float dtcPixelVarReciChn2 = std::max(std::min(dtcPara.pixelVarReciChn2, kFloat16Max), kFloat16Lowest);
    float dtcPixelVarReciChn3 = std::max(std::min(dtcPara.pixelVarReciChn3, kFloat16Max), kFloat16Lowest);

    float pixelVarReciPara[4] = {dtcPixelVarReciChn0, dtcPixelVarReciChn1, dtcPixelVarReciChn2, dtcPixelVarReciChn3};

    HIAI_EXPECT_EXEC(HIAI_NDK_HiAIAippParam_SetDtcMeanPixel(paraBuff, batchIndex, pixelMeanPara, 4));
    HIAI_EXPECT_EXEC(HIAI_NDK_HiAIAippParam_SetDtcMinPixel(paraBuff, batchIndex, pixelMinPara, 4));
    HIAI_EXPECT_EXEC(HIAI_NDK_HiAIAippParam_SetDtcVarReciPixel(paraBuff, batchIndex, pixelVarReciPara, 4));
    return SUCCESS;
}

Status UpdateRotatePara(HiAI_AippParam* paraBuff, uint32_t batchIndex, const RotatePara& rotatePara)
{
    return HIAI_NDK_HiAIAippParam_SetRotationAngle(paraBuff, batchIndex, rotatePara.rotationAngle);
}

void SetAllSwitchsTrue(std::vector<bool> &switchs)
{
    size_t barchCount = switchs.size();
    switchs.assign(barchCount, true);
}

void SetOneSwitchsTrue(std::vector<bool> &switchs, uint32_t batchIndex)
{
    if (batchIndex < switchs.size()) {
        switchs[batchIndex] = true;
    }
}
} // namespace

Status AIPPParaNDKImpl::Init(uint32_t batchCount)
{
    if (paraBuff_ != nullptr) {
        FMK_LOGI("AIPPParaNDKImpl is already inited!");
        return FAILURE;
    }

    HiAI_AippParam* paraBuff = HIAI_NDK_HiAIAippParam_Create(batchCount);
    HIAI_EXPECT_NOT_NULL_R(paraBuff, FAILURE);

    // initial
    paraBuff_ = paraBuff;
    rawBuffer_ = HIAI_NDK_HiAIAippParam_GetData(paraBuff);
    if (InitAippPara(batchCount) != SUCCESS) {
        FMK_LOGE("Init error, InitAippPara is failed!");
        return FAILURE;
    }
    cropSwitchs_.assign(batchCount, false);
    resizeSwitchs_.assign(batchCount, false);
    cscSwitchs_ = false;
    paddingSwitchs_.assign(batchCount, false);
    return SUCCESS;
}

Status AIPPParaNDKImpl::Init(HiAI_AippParam* paraBuff)
{
    HIAI_EXPECT_TRUE(paraBuff_ == nullptr);
    HIAI_EXPECT_TRUE(rawBuffer_ == nullptr);
    HIAI_EXPECT_NOT_NULL(paraBuff);

    void* buffer = HIAI_NDK_HiAIAippParam_GetData(paraBuff);
    if (buffer == nullptr) {
        FMK_LOGE("Init error, paraBuff is not invailed!");
        return FAILURE;
    }

    paraBuff_ = paraBuff;
    rawBuffer_ = buffer;
    uint32_t batchCount = GetBatchCount();
    cropSwitchs_.assign(batchCount, false);
    resizeSwitchs_.assign(batchCount, false);
    cscSwitchs_ = false;
    paddingSwitchs_.assign(batchCount, false);
    return SUCCESS;
}

AIPPParaNDKImpl::~AIPPParaNDKImpl()
{
    HIAI_NDK_HiAIAippParam_Destroy(&paraBuff_);

    paraBuff_ = nullptr;
    rawBuffer_ = nullptr;
    cropSwitchs_.clear();
    resizeSwitchs_.clear();
    paddingSwitchs_.clear();
}

Status AIPPParaNDKImpl::InitAippPara(uint32_t batchCount)
{
    HIAI_EXPECT_TRUE_R(batchCount > 0 && batchCount <= g_maxBatchNum, FAILURE);

    float pixelVarReciPara[4] = {1.0, 1.0, 1.0, 1.0};

    for (uint32_t batchIndex = 0; batchIndex < batchCount; ++batchIndex) {
        HIAI_EXPECT_EXEC(HIAI_NDK_HiAIAippParam_SetDtcVarReciPixel(paraBuff_, batchIndex, pixelVarReciPara, 4));
    }
    return SUCCESS;
}

void* AIPPParaNDKImpl::GetData()
{
    return rawBuffer_;
}

size_t AIPPParaNDKImpl::GetSize() const
{
    return HIAI_NDK_HiAIAippParam_GetDataSize(paraBuff_);
}

uint32_t AIPPParaNDKImpl::GetBatchCount()
{
    return HIAI_NDK_HiAIAippParam_GetBatchCount(paraBuff_);
}

Status AIPPParaNDKImpl::SetInputIndex(uint32_t inputIndex)
{
    if (paraBuff_ == nullptr) {
        FMK_LOGE("SetInputIndex error, AippPara is not inited!");
        return FAILURE;
    }

    return HIAI_NDK_HiAIAippParam_SetInputIndex(paraBuff_, inputIndex);
}

int32_t AIPPParaNDKImpl::GetInputIndex()
{
    if (paraBuff_ == nullptr) {
        FMK_LOGE("GetInputIndex error, AippPara is not inited!");
        return -1;
    }

    int32_t ret = HIAI_NDK_HiAIAippParam_GetInputIndex(paraBuff_);
    if (ret == -1) {
        FMK_LOGE("GetInputIndex error, inner error occurred");
    }
    return ret;
}

Status AIPPParaNDKImpl::SetInputAippIndex(uint32_t inputAippIndex)
{
    if (paraBuff_ == nullptr) {
        FMK_LOGE("SetInputAippIndex error, AippPara is not inited!");
        return FAILURE;
    }

    return HIAI_NDK_HiAIAippParam_SetInputAippIndex(paraBuff_, inputAippIndex);
}

int32_t AIPPParaNDKImpl::GetInputAippIndex()
{
    if (paraBuff_ == nullptr) {
        FMK_LOGE("GetInputAippIndex error, AippPara is not inited!");
        return -1;
    }

    int32_t ret = HIAI_NDK_HiAIAippParam_GetInputAippIndex(paraBuff_);
    if (ret == -1) {
        FMK_LOGE("GetInputAippIndex error, inner error occurred");
    }
    return ret;
}

Status AIPPParaNDKImpl::SetCscPara(ImageFormat targetFormat, ImageColorSpace colorSpace)
{
    HIAI_EXPECT_NOT_NULL_R(paraBuff_, FAILURE);

    ImageFormat inputFormat = GetInputFormat();
    HIAI_EXPECT_TRUE_R(inputFormat != ImageFormat::INVALID, FAILURE);

    std::set<ImageFormat> validFormat = {
        ImageFormat::YVU444SP, ImageFormat::YUV444SP, ImageFormat::RGB888, ImageFormat::BGR888, ImageFormat::YUV400};
    if (validFormat.find(targetFormat) == validFormat.end()) {
        FMK_LOGE("targetFormat is not in valid range of [ YVU444SP, YUV444SP, RGB888, BGR888, YUV400 ]");
        return FAILURE;
    }

    HIAI_EXPECT_TRUE_R(colorSpace != ImageColorSpace::INVALID, FAILURE);
    Status ret = HIAI_NDK_HiAIAippParam_SetCscConfig(paraBuff_, static_cast<HiAI_ImageFormat>(inputFormat),
        static_cast<HiAI_ImageFormat>(targetFormat), static_cast<HiAI_ImageColorSpace>(colorSpace));
    if (ret == SUCCESS) {
        targetFormat_ = targetFormat;
        colorSpace_ = colorSpace;
        cscSwitchs_ = true;
    }
    return ret;
}

Status AIPPParaNDKImpl::SetCscPara(CscMatrixPara cscPara)
{
    HIAI_EXPECT_NOT_NULL_R(paraBuff_, FAILURE);

    ImageFormat inputFormat = ImageFormat::INVALID;
    ImageFormat targetFormat = ImageFormat::INVALID;
    ImageColorSpace colorSpace = ImageColorSpace::INVALID;

    vector<int32_t> cscParaLst = {
        cscPara.matrixR0C0, cscPara.matrixR0C1, cscPara.matrixR0C2,
        cscPara.matrixR1C0, cscPara.matrixR1C1, cscPara.matrixR1C2,
        cscPara.matrixR2C0, cscPara.matrixR2C1, cscPara.matrixR2C2};

    vector<int32_t> bias = {
        cscPara.outputBias0, cscPara.outputBias1, cscPara.outputBias2,
        cscPara.inputBias0, cscPara.inputBias1, cscPara.inputBias2};

    if (ConvertCscMatric2Csc(cscParaLst, inputFormat, targetFormat, colorSpace) != SUCCESS) {
        FMK_LOGE("ConvertCscMatric2Csc fail, please check the csc params");
        return FAILURE;
    }
    if (targetFormat == ImageFormat::RGB888 || targetFormat == ImageFormat::BGR888 ||
        targetFormat == ImageFormat::YUV444SP || targetFormat == ImageFormat::YVU444SP) {
        if (!CheckBiasEqual(bias, targetFormat, colorSpace)) {
            FMK_LOGE("CheckBiasEqual failed");
            inputFormat = ImageFormat::INVALID;
            targetFormat = ImageFormat::INVALID;
            colorSpace = ImageColorSpace::INVALID;
            return FAILURE;
        }
    }

    Status ret = HIAI_NDK_HiAIAippParam_SetCscConfig(paraBuff_, static_cast<HiAI_ImageFormat>(inputFormat),
        static_cast<HiAI_ImageFormat>(targetFormat), static_cast<HiAI_ImageColorSpace>(colorSpace));
    if (ret == SUCCESS) {
        cscMatrixPara_ = cscPara;
        cscSwitchs_ = true;
    }

    return ret;
}

CscPara AIPPParaNDKImpl::GetCscPara()
{
    CscPara cscPara;
    HIAI_EXPECT_NOT_NULL_R(paraBuff_, cscPara);
    HiAI_ImageFormat inputFormat = HIAI_IMAGE_FORMAT_INVALID;
    HiAI_ImageFormat outputFormat = HIAI_IMAGE_FORMAT_INVALID;
    HiAI_ImageColorSpace colorSpace = HIAI_IMAGE_COLOR_SPACE_INVALID;

    Status ret = HIAI_NDK_HiAIAippParam_GetCscConfig(paraBuff_, &inputFormat, &outputFormat, &colorSpace);
    if (ret == SUCCESS && colorSpace != HiAI_ImageColorSpace::HIAI_IMAGE_COLOR_SPACE_INVALID) {
        cscPara.imageFormat = static_cast<ImageFormat>(inputFormat);
        cscPara.outputFormat = static_cast<ImageFormat>(outputFormat);
        cscPara.imageColorSpace = static_cast<ImageColorSpace>(colorSpace);
    }

    return cscPara;
}

CscMatrixPara AIPPParaNDKImpl::GetCscMatrixPara()
{
    return cscMatrixPara_;
}

Status AIPPParaNDKImpl::SetChannelSwapPara(ChannelSwapPara&& channelSwapPara)
{
    HIAI_EXPECT_NOT_NULL_R(paraBuff_, FAILURE);

    HIAI_EXPECT_EXEC(HIAI_NDK_HiAIAippParam_SetChannelSwapConfig(
        paraBuff_, channelSwapPara.rbuvSwapSwitch, channelSwapPara.axSwapSwitch));
    return SUCCESS;
}

ChannelSwapPara AIPPParaNDKImpl::GetChannelSwapPara()
{
    ChannelSwapPara channelSwapPara;
    HIAI_EXPECT_NOT_NULL_R(paraBuff_, channelSwapPara);

    (void)HIAI_NDK_HiAIAippParam_GetChannelSwapConfig(
        paraBuff_, &channelSwapPara.rbuvSwapSwitch, &channelSwapPara.axSwapSwitch);
    return channelSwapPara;
}

Status AIPPParaNDKImpl::SetSingleBatchMultiCrop(bool singleBatchMutiCrop)
{
    HIAI_EXPECT_NOT_NULL_R(paraBuff_, FAILURE);

    HIAI_EXPECT_EXEC(HIAI_NDK_HiAIAippParam_SetSingleBatchMultiCrop(paraBuff_, singleBatchMutiCrop));

    return SUCCESS;
}

bool AIPPParaNDKImpl::GetSingleBatchMultiCrop()
{
    return HIAI_NDK_HiAIAippParam_GetSingleBatchMultiCrop(paraBuff_);
}
// end common interface

template <typename T, typename UpdateParaFunc>
Status AIPPParaNDKImpl::SetAippFuncPara(T&& funcPara, UpdateParaFunc updateParaFunc)
{
    uint32_t batchCount = GetBatchCount();
    HIAI_EXPECT_TRUE_R(batchCount > 0 && batchCount <= g_maxBatchNum, FAILURE);
    HIAI_EXPECT_NOT_NULL_R(paraBuff_, FAILURE);

    for (uint32_t index = 0; index < batchCount; ++index) {
        updateParaFunc(paraBuff_, index, funcPara);
    }
    return SUCCESS;
}

template <typename T, typename UpdateParaFunc>
Status AIPPParaNDKImpl::SetAippFuncPara(uint32_t batchIndex, T&& funcPara, UpdateParaFunc updateParaFunc)
{
    uint32_t batchCount = GetBatchCount();
    HIAI_EXPECT_TRUE_R(batchCount > 0 && batchCount <= g_maxBatchNum, FAILURE);
    HIAI_EXPECT_EXEC(CheckBatchIndex(batchIndex, batchCount));

    HIAI_EXPECT_NOT_NULL(paraBuff_);
    return updateParaFunc(paraBuff_, batchIndex, funcPara);
}

Status AIPPParaNDKImpl::SetCropPara(CropPara&& cropPara)
{
    auto status = SetAippFuncPara(cropPara, UpdateCropPara);
    if (status == SUCCESS) {
        SetAllSwitchsTrue(cropSwitchs_);
    }
    return status;
}

Status AIPPParaNDKImpl::SetCropPara(uint32_t batchIndex, CropPara&& cropPara)
{
    auto status = SetAippFuncPara(batchIndex, cropPara, UpdateCropPara);
    if (status == SUCCESS) {
        SetOneSwitchsTrue(cropSwitchs_, batchIndex);
    }
    return status;
}

CropPara AIPPParaNDKImpl::GetCropPara(uint32_t batchIndex)
{
    CropPara cropPara;
    cropPara.batchIndex = batchIndex;
    (void)HIAI_NDK_HiAIAippParam_GetCropConfig(paraBuff_, batchIndex,
        &cropPara.cropStartPosW, &cropPara.cropStartPosH, &cropPara.cropSizeW, &cropPara.cropSizeH);
    return cropPara;
}

Status AIPPParaNDKImpl::SetResizePara(ResizePara&& resizePara)
{
    auto status = SetAippFuncPara(resizePara, UpdateResizePara);
    if (status == SUCCESS) {
        SetAllSwitchsTrue(resizeSwitchs_);
    }
    return status;
}

Status AIPPParaNDKImpl::SetResizePara(uint32_t batchIndex, ResizePara&& resizePara)
{
    auto status = SetAippFuncPara(batchIndex, resizePara, UpdateResizePara);
    if (status == SUCCESS) {
        SetOneSwitchsTrue(resizeSwitchs_, batchIndex);
    }
    return status;
}

ResizePara AIPPParaNDKImpl::GetResizePara(uint32_t batchIndex)
{
    ResizePara resizePara;
    resizePara.batchIndex = batchIndex;
    (void)HIAI_NDK_HiAIAippParam_GetResizeConfig(
        paraBuff_, batchIndex, &resizePara.resizeOutputSizeW, &resizePara.resizeOutputSizeH);
    return resizePara;
}

Status AIPPParaNDKImpl::SetPaddingPara(PadPara&& paddingPara)
{
    auto status = SetAippFuncPara(paddingPara, UpdatePaddingPara);
    if (status == SUCCESS) {
        SetAllSwitchsTrue(paddingSwitchs_);
    }
    return status;
}

Status AIPPParaNDKImpl::SetPaddingPara(uint32_t batchIndex, PadPara&& paddingPara)
{
    auto status = SetAippFuncPara(batchIndex, paddingPara, UpdatePaddingPara);
    if (status == SUCCESS) {
        SetOneSwitchsTrue(paddingSwitchs_, batchIndex);
    }
    return status;
}

PadPara AIPPParaNDKImpl::GetPaddingPara(uint32_t batchIndex)
{
    PadPara padPara;
    padPara.batchIndex = batchIndex;
    uint32_t chnValue[4] = {0};
    (void)HIAI_NDK_HiAIAippParam_GetPadConfig(paraBuff_, batchIndex,
        &padPara.paddingSizeLeft, &padPara.paddingSizeRight, &padPara.paddingSizeTop, &padPara.paddingSizeBottom);
    (void)HIAI_NDK_HiAIAippParam_GetChannelPadding(paraBuff_, batchIndex, chnValue, 4);

    padPara.paddingValueChn0 = SaveUint16ToFp16(static_cast<uint16_t>(chnValue[0]));
    padPara.paddingValueChn1 = SaveUint16ToFp16(static_cast<uint16_t>(chnValue[1]));
    padPara.paddingValueChn2 = SaveUint16ToFp16(static_cast<uint16_t>(chnValue[2]));
    padPara.paddingValueChn3 = SaveUint16ToFp16(static_cast<uint16_t>(chnValue[3]));

    return padPara;
}

Status AIPPParaNDKImpl::SetDtcPara(DtcPara&& dtcPara)
{
    return SetAippFuncPara(dtcPara, UpdateDtcPara);
}

Status AIPPParaNDKImpl::SetDtcPara(uint32_t batchIndex, DtcPara&& dtcPara)
{
    return SetAippFuncPara(batchIndex, dtcPara, UpdateDtcPara);
}

DtcPara AIPPParaNDKImpl::GetDtcPara(uint32_t batchIndex)
{
    DtcPara dtcPara;
    uint32_t pixelMeanPara[4] = {0};
    float pixelMinPara[4] = {0.0};
    float pixelVarReciPara[4] = {0.0};

    (void)HIAI_NDK_HiAIAippParam_GetDtcMeanPixel(paraBuff_, batchIndex, pixelMeanPara, 4);
    (void)HIAI_NDK_HiAIAippParam_GetDtcMinPixel(paraBuff_, batchIndex, pixelMinPara, 4);
    (void)HIAI_NDK_HiAIAippParam_GetDtcVarReciPixel(paraBuff_, batchIndex, pixelVarReciPara, 4);

    dtcPara.pixelMeanChn0 = static_cast<int16_t>(pixelMeanPara[0]);
    dtcPara.pixelMeanChn1 = static_cast<int16_t>(pixelMeanPara[1]);
    dtcPara.pixelMeanChn2 = static_cast<int16_t>(pixelMeanPara[2]);
    dtcPara.pixelMeanChn3 = static_cast<int16_t>(pixelMeanPara[3]);
    dtcPara.pixelMinChn0 = pixelMinPara[0];
    dtcPara.pixelMinChn1 = pixelMinPara[1];
    dtcPara.pixelMinChn2 = pixelMinPara[2];
    dtcPara.pixelMinChn3 = pixelMinPara[3];
    dtcPara.pixelVarReciChn0 = pixelVarReciPara[0];
    dtcPara.pixelVarReciChn1 = pixelVarReciPara[1];
    dtcPara.pixelVarReciChn2 = pixelVarReciPara[2];
    dtcPara.pixelVarReciChn3 = pixelVarReciPara[3];

    return dtcPara;
}

Status AIPPParaNDKImpl::SetRotatePara(RotatePara&& rotatePara)
{
    return SetAippFuncPara(rotatePara, UpdateRotatePara);
}

Status AIPPParaNDKImpl::SetRotatePara(uint32_t batchIndex, RotatePara&& rotatePara)
{
    return SetAippFuncPara(batchIndex, rotatePara, UpdateRotatePara);
}

RotatePara AIPPParaNDKImpl::GetRotatePara(uint32_t batchIndex)
{
    RotatePara rotatePara;
    rotatePara.batchIndex = batchIndex;

    (void)HIAI_NDK_HiAIAippParam_GetRotationAngle(paraBuff_, batchIndex, &rotatePara.rotationAngle);

    return rotatePara;
}

Status AIPPParaNDKImpl::SetInputFormat(ImageFormat inputFormat)
{
    return HIAI_NDK_HiAIAippParam_SetInputFormat(paraBuff_, static_cast<HiAI_ImageFormat>(inputFormat));
}

ImageFormat AIPPParaNDKImpl::GetInputFormat()
{
    return static_cast<ImageFormat>(HIAI_NDK_HiAIAippParam_GetInputFormat(paraBuff_));
}

std::vector<int32_t> AIPPParaNDKImpl::GetInputShape()
{
    uint32_t srcImageW = 0;
    uint32_t srcImageH = 0;
    HIAI_NDK_HiAIAippParam_GetInputShape(paraBuff_, &srcImageW, &srcImageH);
    std::vector<int32_t> shape;
    shape.push_back(static_cast<int32_t>(srcImageW));
    shape.push_back(static_cast<int32_t>(srcImageH));
    return shape;
}

Status AIPPParaNDKImpl::SetInputShape(std::vector<int32_t>& shape)
{
    if (shape.size() != 2) {
        FMK_LOGE("shape invalid!");
        return FAILURE;
    }
    if (paraBuff_ == nullptr) {
        FMK_LOGE("SetInputShape error, AippPara is not inited!");
        return FAILURE;
    }
    HIAI_NDK_HiAIAippParam_SetInputShape(paraBuff_, static_cast<uint32_t>(shape[0]), static_cast<uint32_t>(shape[1]));

    return SUCCESS;
}

HiAI_AippParam* AIPPParaNDKImpl::GetParaBuffer()
{
    return paraBuff_;
}

bool AIPPParaNDKImpl::GetEnableCsc()
{
    return cscSwitchs_;
}

bool AIPPParaNDKImpl::GetEnableCrop(uint32_t batchIndex)
{
    return batchIndex < cropSwitchs_.size() ? cropSwitchs_[batchIndex] : false;
}

bool AIPPParaNDKImpl::GetEnableResize(uint32_t batchIndex)
{
    return batchIndex < resizeSwitchs_.size() ? resizeSwitchs_[batchIndex] : false;
}

bool AIPPParaNDKImpl::GetEnablePadding(uint32_t batchIndex)
{
    return batchIndex < paddingSwitchs_.size() ? paddingSwitchs_[batchIndex] : false;
}

std::shared_ptr<IAIPPPara> CreateAIPPParaFromNDK(uint32_t batchCount)
{
    shared_ptr<AIPPParaNDKImpl> aippPara = make_shared_nothrow<AIPPParaNDKImpl>();
    if (aippPara == nullptr) {
        FMK_LOGE("create aippParaNDKImpl failed.");
        return nullptr;
    }
    auto status = aippPara->Init(batchCount);
    if (status != SUCCESS) {
        FMK_LOGE("create aippPara failed.");
        return nullptr;
    }
    return aippPara;
}

HiAI_AippParam* GetNNTensorAippParaFromAippPara(const std::shared_ptr<IAIPPPara>& aippPara)
{
    std::shared_ptr<AIPPParaNDKImpl> aippParaImpl = std::dynamic_pointer_cast<AIPPParaNDKImpl>(aippPara);
    if (aippParaImpl == nullptr) {
        FMK_LOGE("invalid aippPara");
        return nullptr;
    }
    return aippParaImpl->GetParaBuffer();
}

bool GetEnableCropFromNDK(std::shared_ptr<IAIPPPara> aippParaBase, uint32_t batchIndex)
{
    HIAI_EXPECT_NOT_NULL_R(aippParaBase, false);
    auto aippParaImpl = std::dynamic_pointer_cast<AIPPParaNDKImpl>(aippParaBase);
    bool cropSwitch = aippParaImpl->GetEnableCrop(batchIndex);
    return cropSwitch;
}

bool GetEnableResizeFromNDK(std::shared_ptr<IAIPPPara> aippParaBase, uint32_t batchIndex)
{
    HIAI_EXPECT_NOT_NULL_R(aippParaBase, false);
    auto aippParaImpl = std::dynamic_pointer_cast<AIPPParaNDKImpl>(aippParaBase);
    bool resizeSwitch = aippParaImpl->GetEnableResize(batchIndex);
    return resizeSwitch;
}

bool GetEnableCscFromNDK(std::shared_ptr<IAIPPPara> aippParaBase)
{
    HIAI_EXPECT_NOT_NULL_R(aippParaBase, false);
    auto aippParaImpl = std::dynamic_pointer_cast<AIPPParaNDKImpl>(aippParaBase);
    bool cscSwitch = aippParaImpl->GetEnableCsc();
    return cscSwitch;
}

bool GetEnablePaddingFromNDK(std::shared_ptr<IAIPPPara> aippParaBase, uint32_t batchIndex)
{
    HIAI_EXPECT_NOT_NULL_R(aippParaBase, false);
    auto aippParaImpl = std::dynamic_pointer_cast<AIPPParaNDKImpl>(aippParaBase);
    bool paddingSwitch = aippParaImpl->GetEnablePadding(batchIndex);
    return paddingSwitch;
}

} // namespace hiai